#include <Windows.h>
#include <stdio.h>


DWORD WINAPI RemoteFunction(LPVOID unused)
{
	MessageBoxA(NULL, "Hello, World!", "Caption", MB_OK);

	ExitThread(0);
	return 0;
}

int main(int argc, char **argv)
{
	PIMAGE_NT_HEADERS pINH;
	PIMAGE_DATA_DIRECTORY pIDD;
	PIMAGE_BASE_RELOCATION pIBR;

	HMODULE hModule;
	HANDLE hProcess, hThread;
	PVOID image, mem;
	DWORD i, count;
	DWORD_PTR delta, OldDelta;
	LPWORD list;
	PDWORD_PTR p;	
	DWORD dwPid = 0;

	if (argc != 2)
	{
		printf("Usage: pe-injection.exe [process_name]\n");
		return 1;
	}

	dwPid = get_process_id(argv[1]); //调用
	if (dwPid == 0)
	{
		printf("Invalid pid");
		return 1;
	}

	hModule = GetModuleHandle(NULL); //返回本身进程句柄
	PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER)hModule;
	pINH = (PIMAGE_NT_HEADERS)((LPBYTE)hModule + pIDH->e_lfanew);
	//文件头位于DOS Stub后面，是一个以PE/0/0为起始标记，由MS-DOS头中e_lfanew字段指向的结构。
	hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);   //以PROCESS_ALL_ACCESS权限打开进程
	if (!hProcess)
	{
		printf("Error: Unable to open target process handle.\n");
		return 1;
	}

	mem = VirtualAllocEx(hProcess, NULL, pINH->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	//分配的空间在指定进程的内存地址空间里
	//pINH->OptionalHeader.SizeOfImage，PE文件被装载到内存空间后总的大小，指从ImageBase到最后一个区块的大小；
	//权限为可读可写
	if (mem == NULL)
	{
		printf("Error: Unable to allocate memory in the target process. %d\n", GetLastError());
		CloseHandle(hProcess);
		return 1;
	}

	printf("Memory allocated. Address: %#010x\n", mem);

	image = VirtualAlloc(NULL, pINH->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	//分配内存空间

	memcpy(image, hModule, pINH->OptionalHeader.SizeOfImage);     //把自己的pe文件装载到内存的数据拷贝到image中

	//Reloc     //地址计算
	pIDD = &pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];   //data directory 包含了PE文件中各重要数据结构的位置和尺寸信息。
																		//IMAGE_DIRECTORY_ENTRY_BASERELOC，重定位表，Windows重建模块的基地址并执行ASLR
																		//绝对地址随着实际加载地址的改变PE提出了重定位表的概念
	pIBR = (PIMAGE_BASE_RELOCATION)((LPBYTE)image + pIDD->VirtualAddress);   

	delta = (DWORD_PTR)((LPBYTE)mem - pINH->OptionalHeader.ImageBase);         //men位置的偏移   正常会被加载到ImageBase对应的地方
	OldDelta = (DWORD_PTR)((LPBYTE)hModule - pINH->OptionalHeader.ImageBase);   //原进程的偏移

	while (pIBR->VirtualAddress != 0)    //VirtualAddress是要修正的数据的基地址
	{
		if (pIBR->SizeOfBlock >= sizeof(IMAGE_BASE_RELOCATION))      //SizeOfBlock是该重定位结构的大小，sizeofBlock后面是一个typeOffset数组
																	//IMAGE_BASE_RELOCATION包括virtualAddress和sizeofblock
		{
			count = (pIBR->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);  //offset数组两个字节为一个
			list = (LPWORD)((LPBYTE)pIBR + sizeof(IMAGE_BASE_RELOCATION));  //到offset数组头部

			for (i = 0; i < count; i++)
			{
				if (list[i] > 0)
				{																						//offset高4位是类型，低12位是修正偏移
					p = (PDWORD_PTR)((LPBYTE)image + (pIBR->VirtualAddress + (0x0fff & (list[i]))));  //需要修改的RVA地址，为typeOffset+virtuakaddress

					*p -= OldDelta;    //进行修正减去原进程的偏移，加上现在的
					*p += delta;
				}
			}
		}

		pIBR = (PIMAGE_BASE_RELOCATION)((LPBYTE)pIBR + pIBR->SizeOfBlock);
	}

	printf("Writing executable image into target process...\n");
	DWORD n;
	if (!WriteProcessMemory(hProcess, mem, image, pINH->OptionalHeader.SizeOfImage, &n))
	{
		printf("Error: Unable to write executable image into target process\n");
		VirtualFreeEx(hProcess, mem, 0, MEM_RELEASE);
		CloseHandle(hProcess);
		VirtualFree(mem, 0, MEM_RELEASE);
		return 1;
	}

	printf("Creating remote thread in target process...\n");
	LPTHREAD_START_ROUTINE remoteThread = (LPTHREAD_START_ROUTINE)((LPBYTE)mem + (DWORD_PTR)(LPBYTE)RemoteFunction - (LPBYTE)hModule);

	hThread = CreateRemoteThread(hProcess, NULL, 0, remoteThread, NULL, 0, NULL);
	if (!hThread)
	{
		printf("Error: Unable to create remote thread in target process.\n");
		VirtualFreeEx(hProcess, mem, 0, MEM_RELEASE);
		CloseHandle(hProcess);
		VirtualFree(image, 0, MEM_RELEASE);
		return 1;
	}

	printf("Thread successfully created! Waiting for the thread to terminate...\n");
	WaitForSingleObject(hThread, INFINITE);

	printf("Thread terminated!\n");
	CloseHandle(hThread);

	printf("Freeing allocated memory...\n");

	VirtualFreeEx(hProcess, mem, 0, MEM_RELEASE);
	CloseHandle(hProcess);
	VirtualFree(image, 0, MEM_RELEASE);
	return 0;
}